﻿using UnityEngine;
using System.Collections.Generic;
using UnityEditor;
using System.IO;
using System.Collections;
using System;
using OfficeOpenXml;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using Xls2Lua;

/// <summary>
/// excel转proto界面逻辑
/// </summary>
public class Excel2ProtoViewTool : BaseEditor
{
    [MenuItem("Tools/导表工具")]
    static void Open()
    {
        GetWindow<Excel2ProtoViewTool>("导表工具");
        ExcelFilePath = PlayerPrefs.GetString(Excel2ProtoTool.ExcelPathKey);
        HotFixCfgPath = PlayerPrefs.GetString(Excel2ProtoTool.HotFixConfigPathKey);
        ResetConfig();
    }

    /// <summary>
    /// 配置表路径
    /// </summary>
    private static string ExcelFilePath;
    /// <summary>
    /// 热更cs放置目录
    /// </summary>
    private static string HotFixCfgPath;

    private static string ExportBytesPath;

    private static string ExportLuaPath;

    /// <summary>
    /// 已经导过的配置表（已经导出bytes）
    /// </summary>
    static List<string> HasExportExcelList = new List<string>();

    /// <summary>
    /// 所有配置表
    /// </summary>
    static List<string> AllExcelList = new List<string>();

    /// <summary>
    /// 新的配置表（没导出bytes）
    /// </summary>
    static List<string> NewExcelList = new List<string>();

    /// <summary>
    /// 是否生成字节数据
    /// </summary>
    private static bool isCreateByte;
    /// <summary>
    /// 是否生成文本数据
    /// </summary>
    private static bool isCreateText;

    public override void OnGUI()
    {
        base.OnGUI();
        RefreshInfo();
        SetPath();
        SetDataType();
        ShowInfo();
        ShowOtherConfig();
    }

    public override void OnEnable()
    {
        ExcelFilePath = PlayerPrefs.GetString(Excel2ProtoTool.ExcelPathKey);
        HotFixCfgPath = PlayerPrefs.GetString(Excel2ProtoTool.HotFixConfigPathKey);
        ExportBytesPath = PlayerPrefs.GetString(Excel2ProtoTool.ExportBytesPathKey);
        ExportLuaPath = PlayerPrefs.GetString(Excel2ProtoTool.ExportLuaDataPathKey);
        HasExportExcelList.Clear();

        AllExcelList.Clear();
        NewExcelList.Clear();
        BuildConfigIndexObject();
    }

    /// <summary>
    /// 注意
    /// </summary>
    void RefreshInfo()
    {
        if (!DrawHeader("注意事项"))
        {
            return;
        }
        BeginContents();
        ShowLabel("1.使用前关闭excel相关软件");
        ShowLabel("2.新表导出，有两步操作，首先“导出Proto”，导出后等编译完（右下角圈圈转完）再操作“导出表数据”");
        ShowLabel("3.旧表改了字段，首先“导出Proto”，导出后等编译完（右下角圈圈转完）再操作“导出表数据”");
        EndContents();
       
    }

    static void ResetConfig()
    {
        HasExportExcelList.Clear();
        AllExcelList.Clear();
        NewExcelList.Clear();
        BuildConfigIndexObject();
    }

    /// <summary>
    /// 生成数据类型
    /// </summary>
    static void SetDataType()
    {
        isCreateByte = PlayerPrefs.HasKey("IsCreateByteData") ? PlayerPrefs.GetInt("IsCreateByteData") == 1 ? true : false : true;
        isCreateText = PlayerPrefs.HasKey("IsCreateTextData") ? PlayerPrefs.GetInt("IsCreateTextData") == 1 ? true : false : true;
        GUILayout.BeginHorizontal();
        isCreateByte = GUILayout.Toggle(isCreateByte, "是否生成字节数据");
        PlayerPrefs.SetInt("IsCreateByteData", isCreateByte ? 1 : 0);
        
        isCreateText = GUILayout.Toggle(isCreateText, "是否生成文本数据");
        PlayerPrefs.SetInt("IsCreateTextData", isCreateText ? 1 : 0);
        GUILayout.EndHorizontal();
    }

    /// <summary>
    /// 设置路径
    /// </summary>
    private void SetPath()
    {
        BeginContents();
        GUILayout.BeginHorizontal();
        TextField("Excel表路径",ref ExcelFilePath);
        if (GUILayout.Button("选择文件夹"))
        {
            ExcelFilePath = EditorUtility.OpenFolderPanel("open res path", ExcelFilePath, "");
            if (!string.IsNullOrEmpty(ExcelFilePath))
            {
                PlayerPrefs.SetString(Excel2ProtoTool.ExcelPathKey, ExcelFilePath);
            }
            HasExportExcelList.Clear();
            AllExcelList.Clear();
            NewExcelList.Clear();
            BuildConfigIndexObject();
        }
        GUILayout.EndHorizontal();

        GUILayout.BeginHorizontal();
        TextField("导出CS路径", ref HotFixCfgPath);
        if (GUILayout.Button("选择文件夹"))
        {
            HotFixCfgPath = EditorUtility.OpenFolderPanel("open res path", HotFixCfgPath, "");
            if (!string.IsNullOrEmpty(HotFixCfgPath))
            {
                PlayerPrefs.SetString(Excel2ProtoTool.HotFixConfigPathKey, HotFixCfgPath);
            }
        }
        GUILayout.EndHorizontal();

        GUILayout.BeginHorizontal();
        TextField("文本字节数据保存路径", ref ExportBytesPath);
        if (GUILayout.Button("选择文件夹"))
        {
            ExportBytesPath = EditorUtility.OpenFolderPanel("open res path", ExportBytesPath, "");
            if (!string.IsNullOrEmpty(ExportBytesPath))
            {
                PlayerPrefs.SetString(Excel2ProtoTool.ExportBytesPathKey, ExportBytesPath);
            }
        }
        GUILayout.EndHorizontal();
        
        GUILayout.BeginHorizontal();
        TextField("Lua数据保存路径", ref ExportLuaPath);
        if (GUILayout.Button("选择文件夹"))
        {
            ExportLuaPath = EditorUtility.OpenFolderPanel("open res path", ExportLuaPath, "");
            if (!string.IsNullOrEmpty(ExportLuaPath))
            {
                PlayerPrefs.SetString(Excel2ProtoTool.ExportLuaDataPathKey, ExportLuaPath);
            }
        }
        GUILayout.EndHorizontal();
        EndContents();
    }
    
    static void BuildConfigIndexObject()
    {
        string serverConfigProtoPath = PlayerPrefs.GetString(Excel2ProtoTool.ExcelPathKey);

        if (!Directory.Exists(ExportBytesPath))
        {
            Debug.LogWarning("文件夹不存在! 请配置好 相关路径!");
            return;
        }
        DirectoryInfo bytesFileDirectoryInfo = new DirectoryInfo(ExportBytesPath);
        FileInfo[] bytesFile = bytesFileDirectoryInfo.GetFiles("*.bytes", SearchOption.AllDirectories);

        foreach (FileInfo fileInfo in bytesFile)
        {
            string str = fileInfo.Name.Replace(".bytes", ".xlsx");
            HasExportExcelList.Add(str);
        }

        DirectoryInfo serverDirectoryInfo = new DirectoryInfo(serverConfigProtoPath);
        FileInfo[] serverFileInfos = serverDirectoryInfo.GetFiles("*.xlsx", SearchOption.TopDirectoryOnly);

        foreach (FileInfo fileInfo in serverFileInfos)
        {
            string str = fileInfo.Name;
            if (Excel2ProtoTool.ingoreList.Contains(str)) { continue; }
            AllExcelList.Add(str);
            if (!HasExportExcelList.Contains(str))
            {
                NewExcelList.Add(str);
            }
        }
    }

    void ShowInfo()
    {
        GUILayout.BeginHorizontal();
        if (GUILayout.Button("一键导出proto"))
        {
            ExportVersionRes_ExcelSelect(AllExcelList);
            AssetDatabase.Refresh();
        }
        if (GUILayout.Button("一键导出表数据Cs"))
        {
            if (CheckExcelIsOpen())
            {
                return;
            }
            Excel2ProtoTool.ExportVersionRes_Excel();
        }
        if (GUILayout.Button("一键导出表数据Lua"))
        {
            if (CheckExcelIsOpen())
            {
                return;
            }
            FileExporter.ExportExcelToLua();
            AssetDatabase.Refresh();
        }
        GUILayout.EndHorizontal();
    }

    void ShowOtherConfig()
    {
        BeginScroll(0, false, true);
        foreach (string s in AllExcelList)
        {
            EditorGUILayout.BeginHorizontal();
            if(NewExcelList.Contains(s))
            {
                GUI.contentColor = Color.green;
                GUILayout.Label("NEW ", GUILayout.Width(40));
                GUI.contentColor = Color.white;
            }

            GUILayout.Label(s, GUILayout.Width(250));
            EditorGUILayout.EndHorizontal();
        }
        EndScroll();
    }

    /// <summary>
    /// 导出多个excel proto,生成cs文件
    /// </summary>
    /// <param name="list"></param>
    static void ExportVersionRes_ExcelSelect(List<string> list)
    {
        if (CheckExcelIsOpen())
        {
            return;
        }

        if (!Excel2ProtoTool.ExportProto(list))
        {
            Debug.LogError("Excel 转 Proto 失败!!!");
            return;
        }
		AssetDatabase.Refresh();
        //将生成的proto导出到cs文件
        GenerateProtoCSfile.GenerateConfigProtoCSfile();
        AssetDatabase.Refresh();
        //Excel2ProtoTool.CopyConfigProtoInExcelPath();
    }

    /// <summary>
    /// 检查excel是否打开
    /// </summary>
    /// <returns></returns>
    static bool CheckExcelIsOpen()
    {
        bool isopen = false;
        foreach (System.Diagnostics.Process p in System.Diagnostics.Process.GetProcesses())
        {
            if (p!=null&&(p.ProcessName == "et" || p.ProcessName == "Excel" || p.ProcessName.Contains("Wps")))
            {
                EditorUtility.DisplayDialog("警告", "excel已经打开,请关闭再操作", "确定");
                isopen = true;
            }
        }
        return isopen;
    }
}

/// <summary>
/// excel转proto工具，主要逻辑在这里
/// </summary>
public class Excel2ProtoTool
{
    /// <summary>
    /// excel路径key
    /// </summary>
    public static string ExcelPathKey
    {
        get
        {
            return Application.dataPath + "ExcelPathKey";
        }
    }

    /// <summary>
    /// excel路径
    /// </summary>
    public static string ExcelPath
    {
        get
        {
            if (PlayerPrefs.HasKey(ExcelPathKey))
            {
                return PlayerPrefs.GetString(ExcelPathKey);
            }
            return "";
        }
    }

    public static string ProtoPath
    {
        get { return $"{ExcelPath}/Proto"; }
    }

    /// <summary>
    /// 放热更cs文件路径key
    /// </summary>
    public static string HotFixConfigPathKey
    {
        get
        {
            return Application.dataPath + "HotFixConfigPathKey";
        }
    }

    /// <summary>
    /// 放热更cs文件路径
    /// </summary>
    public static string HotFixConfigCSPath
    {
        get
        {
            if (PlayerPrefs.HasKey(HotFixConfigPathKey))
            {
                return PlayerPrefs.GetString(HotFixConfigPathKey);
            }
            return "";
        }
    }

    public static string ExportBytesPathKey
    {
        get
        {
            return Application.dataPath + "ExportBytesPathKey";
        }
    }
    
    public static string ExportLuaDataPathKey
    {
        get
        {
            return Application.dataPath + "ExportLuaDataPath";
        }
    }

    /// <summary>
    /// 存放数据文件路径
    /// </summary>
    public static string ExportBytesPath
    {
        get
        {
            if (PlayerPrefs.HasKey(ExportBytesPathKey))
            {
                return PlayerPrefs.GetString(ExportBytesPathKey);
            }
            return "";
        }
    }

    private static string excelToolPath = "";
    public static string ExcelToolPath
    {
        get
        {
            if (string.IsNullOrEmpty(excelToolPath))
            {
                var dirs = Directory.GetDirectories(Application.dataPath, "*", SearchOption.AllDirectories);
                for (int i = 0; i < dirs.Length; i++)
                {
                    if (dirs[i].EndsWith("ExcelTool"))
                    {
                        excelToolPath = dirs[i];
                        return excelToolPath;
                    }
                }
            }
            return excelToolPath;
        }
    }

    public static string ignorePath
    {
        get
        {
            return Application.dataPath + "/Editor/ExportExcel/ignore.txt";
        }
    }

    /// <summary>
    /// 就是导出cs的命名空间，可以自定义
    /// </summary>
    public const string PackageName = "com.CBPK.proto";
    

    /// <summary>
    /// 忽略配置列表
    /// </summary>
    public static List<string> ingoreList = new List<string>();
    
    private static List<FileInfo> ExcelFileInfo = new List<FileInfo>();
    private static List<string> ChangedExcelFile = new List<string>();


    public static void ExportVersionRes_Excel()
    {
        ExcelFileInfo.Clear();
        ChangedExcelFile.Clear();
        //导出配置数据
        ExportExcelConfig();
    }

    /// <summary>
    /// 记录导出文本文件信息
    /// </summary>
    struct fileDataInfo
    {
        public string classType;
        public string fileName;
        public string filePath;

        public fileDataInfo(string classType, string fileName, string filePath)
        {
            this.classType = classType;
            this.fileName = fileName;
            this.filePath = filePath;
        }
    }
    
    /// <summary>
    /// 每个表单独导出一份bytes，也可以给后端用
    /// </summary>
    static void ExportExcelConfig()
    {
        EditorUtility.DisplayProgressBar("Excel 打表进度", "Excel 转 Byte 数据 ...", 0);
        string serverConfigProtoPath = PlayerPrefs.GetString(ExcelPathKey);

        DirectoryInfo serverDirectoryInfo = new DirectoryInfo(serverConfigProtoPath);
        System.IO.FileInfo[] serverFileInfos = serverDirectoryInfo.GetFiles("*.xlsx", SearchOption.TopDirectoryOnly);

        List<fileDataInfo> classNameDic = new List<fileDataInfo>();
        
        bool isCreateByteData = PlayerPrefs.GetInt("IsCreateByteData") == 1;
        bool isCreateTextData = PlayerPrefs.GetInt("IsCreateTextData") == 1;
        EditorUtility.DisplayProgressBar("Excel 打表进度", "Excel 转 Byte 数据 ...", 0.15f);
        int i = 0;
        foreach (System.IO.FileInfo fileInfo in serverFileInfos)
        {
            i++;
            if (fileInfo.Name.StartsWith("~"))
                continue;

            try
            {
                string name = fileInfo.Name.Replace(".xlsx", "");
                var baseBytePath = ExportBytesPath + "/ByteData";
                var pathByteConfig = $"{baseBytePath}/{name}.bytes";
                
                int startIndex = 0;
                string dataPath = "";
                if ((startIndex = pathByteConfig.IndexOf("Resources", 0, StringComparison.Ordinal)) > 0)
                {
                    dataPath = pathByteConfig.Substring(startIndex + 10);
                }
                else
                {
                    startIndex = pathByteConfig.IndexOf("Assets", 0, StringComparison.Ordinal);
                    dataPath = pathByteConfig.Substring(startIndex);
                }

                var typeName = $"{PackageName}.{name}ConfigData";
                classNameDic.Add(new fileDataInfo(typeName, name, dataPath));
                
                var hash = SHA1.Create();
                FileStream stream = File.Open(serverConfigProtoPath + "/" + fileInfo.Name, FileMode.Open, FileAccess.Read);
                var hashCode = Encoding.UTF8.GetString(hash.ComputeHash(stream));
                var hashKey = $"{fileInfo.Name}ExcelConfigDataHashCode";
                var oldHashCode = PlayerPrefs.GetString(hashKey);
                if (oldHashCode.Equals(hashCode))
                {
                    stream.Close();
                    continue;
                }
                
                System.Object configObj = CreateData(stream, name);
                System.IO.MemoryStream MstreamConfig = new System.IO.MemoryStream();
                ProtoBuf.Serializer.Serialize(MstreamConfig, configObj);
                byte[] dataConfig = MstreamConfig.ToArray();

                if (isCreateByteData)
                {
                    if (!Directory.Exists(baseBytePath))
                    {
                        Directory.CreateDirectory(baseBytePath);
                    }
                    
                    using (System.IO.FileStream fStreamConfig = System.IO.File.Create(pathByteConfig))
                    {
                        using (System.IO.BinaryWriter bwConfig =
                            new System.IO.BinaryWriter(fStreamConfig, Encoding.UTF8))
                        {
                            bwConfig.Write(dataConfig);
                            bwConfig.Close();
                        }

                        fStreamConfig.Close();
                    }
                }

                if (isCreateTextData)
                {
                    var basePath = ExportBytesPath + "/TextData";
                    if (!Directory.Exists(basePath))
                    {
                        Directory.CreateDirectory(basePath);
                    }

                    var pathConfig = $"{basePath}/{name}.txt";
                    using (System.IO.FileStream FstreamConfig = System.IO.File.Create(pathConfig))
                    {
                        using (System.IO.StreamWriter bwConfig =
                            new System.IO.StreamWriter(FstreamConfig, Encoding.UTF8))
                        {
                            string data = Encoding.UTF8.GetString(dataConfig);
                            bwConfig.Write(data);
                            bwConfig.Close();
                        }

                        FstreamConfig.Close();
                    }
                }

                MstreamConfig.Close();
                // 数据 生成 成功 才 保存 最新 hash 值
                PlayerPrefs.SetString(hashKey, hashCode);
            }
            catch (Exception e)
            {
                throw new Exception($"数据导出失败,当前数据表: {fileInfo.Name} ... {e}");
            }

            EditorUtility.DisplayProgressBar("Excel 打表进度", "Excel 转 Byte 数据 ...",
                0.15f + Mathf.Clamp((float)i / serverFileInfos.Length, 0, 1) * 0.65f);
        }
        
        var fileConfig = $"{ExportBytesPath}/ConfigDataBase.bytes";
        using (System.IO.FileStream FstreamConfig = System.IO.File.Create(fileConfig))
        {
            using (System.IO.BinaryWriter bwConfig = new System.IO.BinaryWriter(FstreamConfig, Encoding.UTF8))
            {
                StringBuilder stringBuilder = new StringBuilder();
                foreach (var value in classNameDic)
                {
                    stringBuilder.AppendLine($"{value.classType}:{value.fileName}:{value.filePath}");
                }
                bwConfig.Write(Encoding.UTF8.GetBytes(stringBuilder.ToString()));
                bwConfig.Close();
            }
            FstreamConfig.Close();
        }
        
        EditorUtility.DisplayProgressBar("Excel 打表进度", "Excel 转 Byte 数据 ...", 1f);
        EditorUtility.ClearProgressBar();
        
        Debug.Log("Excel表数据导出成功!");
        AssetDatabase.Refresh();
    }

    /// <summary>
    /// 根据excel路径和对应的类名来实例化数据
    /// </summary>
    /// <param name="stream"></param>
    /// <param name="name"></param>
    /// <returns></returns>
    static System.Object CreateData(FileStream stream, string name)
    {
        ExcelPackage excelReader = new ExcelPackage(stream);
        ExcelWorkbook result = excelReader.Workbook;
        ExcelWorksheet workSheet = result.Worksheets.First();
        
        //单个数据
        Type dataType = GetTypeByName(PackageName + "." + name + "Config");
        if (dataType == null)
        {
            Debug.LogError("type====" + name + "===is not find");
            return null;
        }

        //列表数据
        Type configType = GetTypeByName(PackageName + "." + name + "ConfigData");
        if (configType == null)
        {
            Debug.LogError("type=====" + name + "Config=======is not find");
            return null;
        }

        //获取列表变量
        System.Reflection.FieldInfo field = configType.GetField("_config",
            System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.FlattenHierarchy |
            System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Instance |
            System.Reflection.BindingFlags.GetField);


        if (field == null)
        {
            Debug.LogError("field not find !!! ======" + configType.Name + "._config");
            return null;
        }

        #region 遍历整个excel表读取每一行数据（可以扩展列表，枚举，其他表数据，这里只列出基本数据类型）
        int columns = workSheet.Dimension.End.Column;
        int rows = workSheet.Dimension.End.Row;
        System.Reflection.PropertyInfo[] tmpFileds = dataType.GetProperties(System.Reflection.BindingFlags.SetField | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);

        System.Object configObj = Activator.CreateInstance(configType);

        IList m_DataList = field.GetValue(configObj) as IList;
        for (int i = 0; i < rows; i++)
        {
            if (i > 2)
            {
                System.Object target = Activator.CreateInstance(dataType);
                if (columns > 0)
                {
                    string dd = workSheet.GetValue<string>(i + 1, 1);
                    if (string.IsNullOrEmpty(dd))
                    {
                        break;
                    }
                }

                for (int j = 0, FiledsIndex = 0; j < columns; j++)
                {
                    string kk = workSheet.GetValue<string>(i + 1, j + 1);

                    if (FiledsIndex >= tmpFileds.Length)
                    {
                        continue;
                    }

                    TypeCode tpy = Type.GetTypeCode(tmpFileds[FiledsIndex].PropertyType);

                    string value = workSheet.GetValue<string>(i + 1, j + 1);
                    if (string.IsNullOrEmpty(value))
                    {
                        value = "";
                    }
                    value = value.TrimEnd(' ');
                    if (!tmpFileds[FiledsIndex].CanWrite)
                    {
                        continue;
                    }
                    switch (tpy)
                    {
                        case TypeCode.Int32:

                            if (kk != null)
                            {

                                if (string.IsNullOrEmpty(value))
                                {
                                    value = "0";
                                }
                                try
                                {
                                    tmpFileds[FiledsIndex].SetValue(target, Int32.Parse(value), null);
                                }
                                catch (System.Exception ex)
                                {
                                    Debug.LogError(ex.ToString());
                                    Debug.LogError(string.Format("Data error: {0} : {2}:[{1}] is not int", name, workSheet.GetValue<string>(i + 1, j + 1), tmpFileds[j].Name));

                                    string key = workSheet.GetValue<string>(i + 1, 1);
                                    int keyValue;
                                    if (int.TryParse(key, out keyValue))
                                    {
                                        Debug.LogError("上条错误对应的ID：" + keyValue);
                                    }
                                }

                            }
                            else
                            {
                                tmpFileds[FiledsIndex].SetValue(target, 0, null);
                            }

                            break;

                        case TypeCode.String:
                            if (kk != null)
                            {
                                tmpFileds[FiledsIndex].SetValue(target, workSheet.GetValue<string>(i + 1, j + 1), null);
                            }
                            else
                            {
                                tmpFileds[FiledsIndex].SetValue(target, "", null);
                            }
                            break;

                        case TypeCode.Single:
                            if (kk != null)
                            {
                                try
                                {

                                    if (string.IsNullOrEmpty(value))
                                    {
                                        value = "0";
                                    }
                                    tmpFileds[FiledsIndex].SetValue(target, float.Parse(value), null);
                                }
                                catch (System.Exception ex)
                                {
                                    Debug.LogError(ex.ToString());
                                    Debug.LogError(string.Format("Data error: {0} : {2}:[{1}] is not float", name, workSheet.GetValue<string>(i + 1, j + 1), tmpFileds[j].Name));
                                }

                            }
                            else
                            {
                                tmpFileds[FiledsIndex].SetValue(target, 0, null);
                            }

                            break;
                        case TypeCode.Boolean:
                            tmpFileds[FiledsIndex].SetValue(target, workSheet.GetValue<string>(i + 1, j + 1), null);
                            break;
                        default:
                            break;
                    }

                    FiledsIndex++;
                }

                m_DataList.Add(target);

            }
        }
        #endregion

        #region 校验数据
        stream.Close();
        #endregion
        return configObj;
    }

    /// <summary>
    /// 导出proto文件
    /// </summary>
    /// <param name="platform"></param>
    /// <param name="selectList"></param>
    public static bool ExportProto(List<string> selectList = null)
    {
        EditorUtility.DisplayProgressBar("Excel 打表进度", "Excel 转 Proto 文件 ...", 0);
        string serverConfigProtoPath = PlayerPrefs.GetString(ExcelPathKey);

        DirectoryInfo serverDirectoryInfo = new DirectoryInfo(serverConfigProtoPath);
        System.IO.FileInfo[] serverFileInfos = serverDirectoryInfo.GetFiles("*.xlsx", SearchOption.TopDirectoryOnly);

        //可导配置列表
        List<string> excelNames = new List<string>();
        //可导配置相关信息（用于创建初始化代码）
        List<CreateConfigScriptData> scriptDataList = new List<CreateConfigScriptData>();
        EditorUtility.DisplayProgressBar("Excel 打表进度", "Excel 转 Proto 文件 ...", 0.15f);
        #region 遍历所有配置导出单个proto文件
        int i = 0;
        foreach (System.IO.FileInfo fileInfo in serverFileInfos)
        {
            i++;
            if (fileInfo.Name.StartsWith("~"))
                continue;

            if (Excel2ProtoTool.ingoreList.Contains(fileInfo.Name)) { continue; }

            string name = fileInfo.Name.Replace(".xlsx", "");

            if (!fileInfo.Name.EndsWith(".xlsx"))
            {
                Debug.Log("跳过文件  " + name + " 因为它不是一个表文件");
                continue;
            }
            //单个配置相关数据
            CreateConfigScriptData scriptData = new CreateConfigScriptData();
            scriptDataList.Add(scriptData);
            scriptData.ExcelName = name;
            excelNames.Add(name);

            if (selectList != null && !selectList.Contains(fileInfo.Name))
            {
                continue;
            }

            Debug.Log("build Proto:" + name);

            var start = Time.realtimeSinceStartup;

            FileStream stream = File.OpenRead(serverConfigProtoPath + "/" + fileInfo.Name);
            
            ExcelPackage excelReader = new ExcelPackage(stream);
            ExcelWorkbook result = excelReader.Workbook;
            ExcelWorksheet workSheet = result.Worksheets.First();
            if (workSheet.Dimension == null)
            {
                Debug.LogError($"表: {fileInfo.FullName} 数据为空 !");
                return false;
            }
            int columns = workSheet.Dimension.End.Column;
            int rows = workSheet.Dimension.End.Row;

            if (rows < 3)
            {
                Debug.LogError("选择的excel表行数小于4");
                return false;
            }
            string SaveValue = "package " + PackageName + ";\n\n";
            SaveValue += "message " + name + "Config{\n";

            for (int j = 0; j <= columns; j++)
            {
                if(j == 0) { continue; }
                if (string.IsNullOrEmpty(workSheet.GetValue<string>(1, j)))
                {
                    continue;
                }
                string valueName = workSheet.GetValue<string>(3, j);
                if (string.IsNullOrEmpty(valueName))
                {
                    Debug.LogError(fileInfo.Name + "第" + (j + 1) + "列变量名为空");
                    return false;
                }
                valueName = valueName.TrimEnd(' ');
                string explain = workSheet.GetValue<string>(1, j);
                string type = workSheet.GetValue<string>(2, j);
                //保存第一个字段的类型以及变量名
                if (j == 1)
                {
                    scriptData.VariableName = valueName;
                    scriptData.TypeName = type;
                }
                if (type == "int")
                    type = "int32";
                else if (type == "long")
                    type = "int64";
                SaveValue += "\trequired " + type + " " + valueName + " = " + (j) + ";\t\t//" + explain + "\n";
            }

            SaveValue += "}\n\n";

            SaveValue += "message " + name + "ConfigData{\n";
            SaveValue += "\trepeated " + name + "Config config = 1;\n";
            SaveValue += "}\n";

            if (!Directory.Exists(ProtoPath))
            {
                Directory.CreateDirectory(ProtoPath);
            }
            
            System.IO.FileStream Fstream = System.IO.File.Create(ProtoPath + "/" + name + ".proto");
            char[] data = SaveValue.ToCharArray();
            System.IO.BinaryWriter bw = new System.IO.BinaryWriter(Fstream);
            bw.Write(data);
            bw.Close();
            Fstream.Close();

            var end = Time.realtimeSinceStartup;
            EditorUtility.DisplayProgressBar("Excel 打表进度", "Excel 转 Proto 文件 ...",
                0.15f + Mathf.Clamp((float)i / serverFileInfos.Length, 0, 1) * 0.65f);
            Debug.LogFormat("Build,Start:{0}s end:{1}s duration:{2}s name:{3}", start, end, end - start, name);
        }
        #endregion

        #region 自动补齐初始化代码
        CreateConfigScrpt(scriptDataList);
        #endregion
        return true;
    }
    
    public static void CreateConfigScrpt(List<CreateConfigScriptData> scriptDataList)
    {
        EditorUtility.DisplayProgressBar("Excel 打表进度", "生成数据集 ConfigData.cs 文件 ...", 0.8f);
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.AppendLine("// 当前 脚本 由工具自动生成! 不要手动修改!!!");
        stringBuilder.AppendLine($"// CreateTime: {System.DateTime.Now}");
        stringBuilder.AppendLine("");
        stringBuilder.AppendLine("using System.Collections.Generic;");
        stringBuilder.AppendLine("using ExcelTool;");
        stringBuilder.AppendLine($"namespace {PackageName}");
        stringBuilder.AppendLine("{");
        for (int i = 0; i < scriptDataList.Count; i++)
        {
            CreateConfigScriptData tempData = scriptDataList[i];
            stringBuilder.AppendLine($"\tpublic partial class {tempData.ExcelName}ConfigData : IConfigData");
            stringBuilder.AppendLine("\t{");
            stringBuilder.AppendLine($"\t\tprivate static Dictionary<{tempData.TypeName}, {tempData.ExcelName}Config> _{tempData.ExcelName}Dic;");
            stringBuilder.AppendLine("\t\tpublic global::ProtoBuf.IExtensible LoadDataById(int id) {");
            stringBuilder.AppendLine($"\t\t\treturn _{tempData.ExcelName}Dic[id];");
            stringBuilder.AppendLine("\t\t}");
            stringBuilder.AppendLine("\t\tpublic List<global::ProtoBuf.IExtensible> DataList()");
            stringBuilder.AppendLine("\t\t{");
            stringBuilder.AppendLine("\t\t\tList<global::ProtoBuf.IExtensible> dataList = new List<global::ProtoBuf.IExtensible>();");
            stringBuilder.AppendLine($"\t\t\tforeach (var value in _{tempData.ExcelName}Dic.Values)");
            stringBuilder.AppendLine("\t\t\t{ dataList.Add(value); }");
            stringBuilder.AppendLine("\t\t\treturn dataList;");
            stringBuilder.AppendLine("\t\t}");
            stringBuilder.AppendLine($"\t\tpublic Dictionary<{tempData.TypeName}, {tempData.ExcelName}Config> {tempData.ExcelName}Dic");
            stringBuilder.AppendLine("\t\t{ get {");
            stringBuilder.AppendLine($"\t\t\t\treturn _{tempData.ExcelName}Dic;");
            stringBuilder.AppendLine("\t\t\t} }");
            stringBuilder.AppendLine("\t\tpublic void InitData() {");
            stringBuilder.AppendLine($"\t\t\t_{tempData.ExcelName}Dic = new Dictionary<{tempData.TypeName}, {tempData.ExcelName}Config>();");
            stringBuilder.AppendLine("\t\t\tforeach (var value in config) {");
            stringBuilder.AppendLine($"\t\t\t\t_{tempData.ExcelName}Dic.Add(value.{tempData.VariableName}, value);");
            stringBuilder.AppendLine("\t\t\t} }");
            stringBuilder.AppendLine("\t}");
        }
        stringBuilder.AppendLine("}");
        stringBuilder.AppendLine("public class ConfigList");
        stringBuilder.AppendLine("{");
        for (int i = 0; i < scriptDataList.Count; i++)
        {
            stringBuilder.AppendLine(
                $"\tpublic static readonly string {scriptDataList[i].ExcelName}Name = \"{scriptDataList[i].ExcelName}\";");
        }
        stringBuilder.AppendLine("}");
        string filePath = HotFixConfigCSPath + "/ConfigData.cs";
        if (File.Exists(filePath))
        {
            File.Delete(filePath);
        }
        FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate);
        //获得字节数组
        byte[] wiritedata = System.Text.Encoding.UTF8.GetBytes(stringBuilder.ToString());
        //开始写入
        fs.Write(wiritedata, 0, wiritedata.Length);
        //清空缓冲区、关闭流
        fs.Flush();
        fs.Close();
        EditorUtility.DisplayProgressBar("Excel 打表进度", "完成", 1);
        EditorUtility.ClearProgressBar();
    }

    public static void CopyConfigProtoInExcelPath()
    {
        string serverConfigProtoPath = PlayerPrefs.GetString(ExcelPathKey);
        if (string.IsNullOrEmpty(serverConfigProtoPath))
        {
            return;
        }
        
        DirectoryInfo serverDirectoryInfo = new DirectoryInfo(serverConfigProtoPath);
        System.IO.FileInfo[] serverFileInfos = serverDirectoryInfo.GetFiles("*.proto", SearchOption.TopDirectoryOnly);
        foreach (System.IO.FileInfo fi in serverFileInfos)
        {
            if (fi.Name.EndsWith(".proto"))
            {
                fi.Delete();
            }
        }

        string[] allExcelFiles = System.IO.Directory.GetFiles(serverConfigProtoPath, "*.xlsx", SearchOption.AllDirectories);
        List<string> protoNames = new List<string>();
        foreach (string str in allExcelFiles)
        {
            if (str.StartsWith("~"))
                continue;

            string temp = str.Remove(0, serverConfigProtoPath.Length + 1);
            temp = temp.Remove(temp.Length - 5, 5);
            temp += ".proto";
            protoNames.Add(temp);
        }

        string unityConfigProtoPath = ProtoPath;
        DirectoryInfo unityDirectoryInfo = new DirectoryInfo(unityConfigProtoPath);
        System.IO.FileInfo[] unityFileInfos = unityDirectoryInfo.GetFiles("*.proto", SearchOption.TopDirectoryOnly);
        foreach (System.IO.FileInfo fi in unityFileInfos)
        {
            if (protoNames.Contains(fi.Name) || fi.Name == "ConfigDatabaseFile.proto")
            {
                fi.CopyTo(serverConfigProtoPath + "/" + fi.Name);
            }
        }
    }
    /// <summary>
    /// 字符获取类型
    /// </summary>
    /// <param name="str"></param>
    /// <returns></returns>
    public static Type GetTypeByName(string str)
    {
        Type typ = Type.GetType(str + ",Assembly-CSharp-firstpass");
        if (typ == null)
        {
            typ = Type.GetType(str + ",Assembly-CSharp");
            if (typ == null)
            {
                Debug.LogError("not find!!! ");
                return null;
            }
        }
        return typ;
    }
}
